package simulationEngine;

import host.HostList;

import java.util.Vector;

import javax.swing.JOptionPane;

import miniworld.Miniworld;
import simulationEnviroment.SimulationEnviroment;
import ui.SimulationExecutionCardContainer;
import datatypes.DiscoveryMechanismEnum;
import datatypes.ScenarioDescription;

public class SimulationEngine {
//Variables
	private HostList hostList = null;
	private SimulationEnviroment simEnviroment = null;
	private Miniworld mw = null;
	private Scheduler scheduler = null;
	private ScenarioDescription scenDesc = null;
	private static int simulationID = 0;
	private int simulationIDinDB = -1;
	private int simulationInstance = 0;
	private static Vector<SimulationEngine> engines = null;
	private SimulationExecutionCardContainer gui = null;
	private DiscoveryMechanismEnum discMechanism = null;
	private static DiscoveryMechanismEnum nextSimulationdiscMechanism = null;
	
	private int numberOfStepsToSimulate = 1;
	private int stepIterator = 1;
	
	private boolean isRunning = false;
	//first time a simulation isrunning it will be started, the second time it is already started
	private boolean isStarted = false;

//Methods	
	//BEGIN CONSTRUCTOR-PART
	public SimulationEngine(int SimulationIDinDB, ScenarioDescription sd, SimulationExecutionCardContainer gui){
		simulationIDinDB = SimulationIDinDB;
		discMechanism = gui.mech;
		this.scenDesc = sd;
		this.gui = gui;
		this.simulationInstance = simulationID;
		simulationID++;
		if (engines == null){
			engines = new Vector<SimulationEngine>();
		}
		
		engines.add(this);
		
	}
	
	public static SimulationEngine getEngine (int Instance){
		if (engines == null){
			return null;
		}
		for (int i=0; i < engines.size(); i++ ){
			if (engines.get(i).simulationInstance == Instance){
				return engines.get(i);
			}
		}
		return null;
	}
	
	//END CONSTRUCTOR PART
	
	
	public SimulationExecutionCardContainer getGUI (){
		return this.gui;
	}
	
	
	public ScenarioDescription getScenarioParameter(){
		return this.scenDesc;
	}
	
	public static int getNextEngineID(){
		return simulationID;
	}
	
	
	/** This method returns null, if no simDesc is given
	 * 
	 * @return null if no SimDesc is given ; Simlalation Enviroment otherwise
	 */
	public SimulationEnviroment getSimulationEnviroment(){
		if (simEnviroment == null && scenDesc != null){
			simEnviroment = new SimulationEnviroment(this);
		}
		return simEnviroment;
	}

	public Miniworld getMiniworld() {
		if (mw == null) {
			mw = new Miniworld(this);
		}
		return mw;
	}

	public void setMiniworld(Miniworld nmw) {
		this.mw = nmw;
	}

	public HostList getHostList (){
		if (hostList == null){
			hostList = new HostList();
			this.hostList.build(scenDesc);
		}
		return hostList;
	}
	
	public int getSimulationIDinDB (){
		return simulationIDinDB;
	}
	
	
	public Scheduler getScheduler(){
		if (this.scheduler == null){
			scheduler = new Scheduler(this);
		}
		return this.scheduler;
	}
	
	//and now the intresting Part,... the algorithm of the Simulation
	public void startSimulation(int numberOfSteps){
		if (!isRunning){
			this.isRunning = true;
			this.numberOfStepsToSimulate = numberOfSteps;
			this.stepIterator = 1;
			
			if (!isStarted){
				this.isStarted = true;
				//At first if not already done initialise all for Simulation needed Components
				this.getHostList();
				this.getMiniworld();
				this.getScheduler();
				this.getSimulationEnviroment();	
				
				//init Roster
				this.getSimulationEnviroment().getProcessor().invokeMiniworldInitialisation();
				//Now everything is initialised, we can focus on starting Simulation in SimulationEnviroment
				this.getSimulationEnviroment().startSimulation();
			} else {
				this.getSimulationEnviroment().getProcessor().prepareEnviromentForNextStep();
				this.getSimulationEnviroment().getClock().onProcessorReady();
			}
			
		}
	}
	
	public void stopSimulation(){
		if (!isRunning){
			onSimulationIsStopped();
		}
	}
	
	public void isFinished (){
		this.isRunning = false;
		JOptionPane.showMessageDialog(null,
				"Simulation des Szenarios: \"" + this.getScenarioParameter().getScenarioName() + "\" beendet!", "Simulation beendet",
				JOptionPane.INFORMATION_MESSAGE);
	}
	
	public boolean isSequenceComplete(){
		if (this.stepIterator > this.numberOfStepsToSimulate){
			this.isRunning = false;
			return true;
		}
		return false;
	}
	
	public int getID(){
		return this.simulationInstance;
	}
	
	
	public void onSimulationIsStopped(){
		hostList = null;
		simEnviroment = null;
		mw = null;
		scheduler = null;
		isRunning = false;
	}

	public void countStepIsDone (){
		this.stepIterator++;
	}

	
	/**
	 * @return the discMechansim
	 */
	public DiscoveryMechanismEnum getDiscMechansim() {
		if (discMechanism == null){
			discMechanism = nextSimulationdiscMechanism;
		}
		return discMechanism;
	}
	/**
	 * @return the discMechansim
	 */
	public static DiscoveryMechanismEnum getNextDiscMechanism() {
		return nextSimulationdiscMechanism;
	}
	/**
	 * @return the discMechansim
	 */
	public static void setNextDiscMechansim(DiscoveryMechanismEnum nextMech) {
		nextSimulationdiscMechanism = nextMech;
	}
}
